﻿using System.Linq;
using CsvHelper.Configuration;

namespace YnabCsvConverter.Core.Converters
{
   using System;
   using System.Collections.Generic;
   using System.Globalization;
   using System.IO;
   using System.Text;
   using CsvHelper;

   /// <summary>
   /// Base Converter
   /// </summary>
   public class BaseConverter
   {
      /// <summary>
      /// Initializes a new instance of the <see cref="BaseConverter" /> class.
      /// </summary>
      public BaseConverter()
      {
         SourceCulture = CultureInfo.InvariantCulture;
      }

      /// <summary>
      /// Gets or sets the source culture.
      /// </summary>
      /// <value>
      /// The source culture.
      /// </value>
      public CultureInfo SourceCulture { get; set; }

      /// <summary>
      /// Saves the YNAB records as CSV.
      /// </summary>
      /// <param name="ynabRecords">The ynab records.</param>
      /// <param name="targetFilename">The target filename.</param>
      /// <param name="mode">The mode.</param>
      /// <exception cref="System.NotImplementedException"></exception>
      public void WriteRecords(IEnumerable<YnabRecord> ynabRecords, string targetFilename, CreationMode mode)
      {
         switch (mode)
         {
            case CreationMode.All:
               WriteAllRecords(ynabRecords, targetFilename);
               break;

            case CreationMode.Delta:
               WriteDeltaRecordes(ynabRecords, targetFilename);
               break;

            default:
               throw new NotSupportedException("CreationMode not supported.");
         }
      }

      /// <summary>
      /// Writes the delta recordes.
      /// </summary>
      /// <param name="ynabRecords">The ynab records.</param>
      /// <param name="targetFilename">The target filename.</param>
      /// <exception cref="System.NotImplementedException"></exception>
      private void WriteDeltaRecordes(IEnumerable<YnabRecord> ynabRecords, string targetFilename)
      {
         // Read existing YNAB Records.
         var existingYnabRecords = (List<YnabRecord>) ReadRecords<YnabRecord>(targetFilename, ',', true);

         // Check if record to write already exists. If so it will be ignored. Only new records will be added
         // to the delta records list.
         var deltaRecords = ynabRecords.Where(ynabRecord => !existingYnabRecords.Contains(ynabRecord)).ToList();

         WriteAllRecords(deltaRecords, targetFilename);
      }

      /// <summary>
      /// Writes all records.
      /// </summary>
      /// <param name="ynabRecords">The ynab records.</param>
      /// <param name="targetFilename">The target filename.</param>
      /// <exception cref="System.NotImplementedException"></exception>
      private void WriteAllRecords(IEnumerable<YnabRecord> ynabRecords, string targetFilename)
      {
         using (var streamWriter = new StreamWriter(targetFilename, false, Encoding.UTF8))
         {
            var csv = new CsvWriter(streamWriter);

            // Write Header Record
            var header = new YnabRecord();
            csv.WriteRecord(header);

            foreach (var ynabLine in ynabRecords)
            {
               csv.WriteRecord(ynabLine);
            }
         }
      }

      /// <summary>
      /// Reads records from a csv file.
      /// </summary>
      /// <typeparam name="T"></typeparam>
      /// <param name="fileName">The filename.</param>
      /// <param name="delimiter">The delimiter. (for example comma [,] or semicolon [;])</param>
      /// <param name="hasHeaderRecord">Set if the first line of the file represents the header. 
      /// If set to true, the first line will be ignored.</param>
      /// <returns>List of Records.</returns>
      public IEnumerable<T> ReadRecords<T>(string fileName, char delimiter, bool hasHeaderRecord)  where T : class
      {
         var records = new List<T>();

         var config = new CsvConfiguration { Delimiter = delimiter, HasHeaderRecord = hasHeaderRecord };
         
         using (var streamReader = new StreamReader(fileName, Encoding.Default))
         {
            var csvReader = new CsvReader(streamReader, config);

            while (csvReader.Read())
            {
               var record = csvReader.GetRecord<T>();
               records.Add(record);
            }
         }

         return records;
      }
   }
}
